package mrusanov.tournament

import mrusanov.team.Team
import java.util.Date

class RoundRobinTournamentRound(clubsCount:Int, groupsCount:Int, passedFromGroup:Int, roundsCount:Int, dates:Vector[Date],
                                reward:RoundRobinTournamentReward = new RoundRobinTournamentReward(p => 0))
    extends TournamentRound(clubsCount, reward) {
  val clubsPerGroup = clubsCount / groupsCount
  require(dates.size == roundsCount * (clubsPerGroup - 1), "Dates size must be equal to " + (roundsCount * (clubsPerGroup - 1)))
  require(clubsCanBeDividedByGroups, "Cann't divide clubs by groups. There are "
    + clubsCount + " clubs and " + groupsCount)
  var groups:Map[Int, List[Team]] = Map()

  override def initMatches() {
    var remainingTeams = teams
    for (i <- 0.until(groupsCount)) {
      val groupAndRest = takeAndRemoveElements(clubsCount / groupsCount, remainingTeams)
      val group = groupAndRest._1
      remainingTeams = groupAndRest._2
      val groupMatches = getRoundRobinMatches(group)
      _matches = uniteMatchDays(_matches, groupMatches)
      groups += (i -> group)
    }
  }

  private[this] def gamesCount:Int = {
    val clubsInGroup = clubsCount / groupsCount
    (clubsInGroup * clubsInGroup - clubsInGroup) / 2 * roundsCount
  }

  override def resultsForReward:List[Team] = {
    val resultsInGroups = groups.values map(groupResult(_))
    val withIndex = resultsInGroups.map(_.zipWithIndex).flatten
    withIndex.toList.sortBy(_._2).map(_._1.team)
  }

  private[this] def groupResult(group:List[Team]):List[RoundRobinTournamentResult] = {
     val groupResult = roundRobinResults(group)
     groupResult.sorted.reverse
  }

  override def passedRound:List[Team] = {
    require(isOver, "Tournament must be isOver!")
    resultsForReward.take(groupsCount * passedFromGroup)
  }

  private[this] def clubsCanBeDividedByGroups:Boolean = {
    clubsCount % groupsCount == 0
  }

  private[this] def getRoundRobinMatches(clubs:List[Team]):Map[Int, List[MatchInfo]] = {
    var retMap = Map[Int, List[MatchInfo]]()
    var rotatedClubs = clubs
    var firstClubsHostGame = true
    for (j <- 0.until(roundsCount)) {
      val startingRound = j * (clubs.size - 1)
      val endingRound = (j + 1) * (clubs.size - 1)
      for (i <- startingRound until endingRound ) {
        val matches = getMatches(rotatedClubs, dates(i), firstClubsHostGame)
        retMap += (i -> matches)
        rotatedClubs = rotateClubs(rotatedClubs)
      }
      firstClubsHostGame ^= true
    }

    retMap
  }

  /**
   * read to understand: http://en.wikipedia.org/wiki/Round-robin_tournament#Scheduling_algorithm
   *
   */
  private[this] def rotateClubs(list:List[Team]):List[Team] = {
    val firstElement = list.head
    val listWithoutFirstElement = list.filterNot(_ == firstElement)
    val newFirstElement = listWithoutFirstElement.head
    val listWithoutTwoFirstElements = listWithoutFirstElement.filterNot(p => p == newFirstElement)
    val rotatedListWithoutFirstElement = listWithoutTwoFirstElements :+ newFirstElement
    firstElement :: rotatedListWithoutFirstElement
  }

  private[this] def takeAndRemoveElements[T](count:Int, list:List[T]):(List[T], List[T]) = {
    val takenElements = list.take(count)
    val retList = list.filterNot(takenElements.contains(_))
    (takenElements, retList)
  }

  def roundRobinResults(clubs:List[Team]):List[RoundRobinTournamentResult] = clubs.map(c => new RoundRobinTournamentResult(c, roundRobinResultsForClub(c)))

  private[this] def roundRobinResultsForClub(team:Team):List[MatchInfo] = allMatches.filter(_.tookPart(team)).filter(_.isOver)

}


